<?php

namespace app\traits;

use app\modules\Admin\models\scoregoodsModel;
use app\modules\Admin\service\Redis as RedisService;
use app\service\auth;
use app\service\config;
use Illuminate\Database\Capsule\Manager as DB;

trait orderoperation
{
    // 生成订单编号
    public static function generateSn($user_id)
    {
        $rand = $user_id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999);
        $order_sn = date('Yhis') . $rand;

        $id = str_pad($user_id, (24 - strlen($order_sn)), '0', STR_PAD_BOTH);

        return $order_sn . $id;
    }

    // 获取可用优惠券列表
    public static function coupons($goods_list, $goods_amount = 0)
    {
        // 商品总金额
        if (!$goods_amount) {
            // 下单的时候把计算好的 goods_amount 传进来了，接口直接获取可用列表的时候，需要计算
            foreach ($goods_list as $k => $goods) {
                $goods_amount += ($goods['goods_price'] * $goods['goods_num']);
            }
        }

        $coupons = \couponsModel::getCouponsList(\couponsModel::COUPONS_CAN_USE);

        $new_coupons = [];
        // 过滤，如果有一个产品不适用，则优惠券不允许使用,不显示
        foreach ($coupons as $key => $coupon) {
            if ($coupon['goods_ids'] === '0') {
                // 所有商品可用
                $can_use = true;
            } else {
                $goods_ids = explode(',', $coupon['goods_ids']);

                $can_use = true;
                foreach ($goods_list as $k => $goods) {
                    if (!in_array($goods['goods_id'], $goods_ids)) {
                        $can_use = false;
                        break;
                    }
                }
            }

            // 商品可用 并且 商品金额满足
            if ($can_use && $coupon->enough <= $goods_amount) {
                $new_coupons[] = $coupon;
            }
        }

        $new_coupons = array_values($new_coupons);

        return $new_coupons;
    }


    public function setExt($order, $field, $origin = [])
    {
        $newExt = array_merge($origin, $field);

        $orderExt = $order['ext_arr'];

        return array_merge($orderExt, $newExt);
    }




    /************************************* 订单预览 start *************************************/

    // 计算订单
    public static function previewOrder($params, $calc_type = 'pre')
    {
        // 获取请求参数
        list(
            $goods_list,
            $order_type,
            $groupon_id,
            $buy_type,
            $address_id,
            $coupons_id,
            $activity_id,
            $from,
            $off_price,
            $hongbao,
            $quanyi,
            ) = self::preParams($params);

        // 检测并重新组装要购买的商品列表
        list(
            $new_goods_list,            // 组合好的新的商品列表
            $activity_type,             // 订单参与的活动类型，后面还需计算一次
            $activity_discounts,        // 订单可能参与的所有活动
            $need_address,              // 是否需要收货地址
            $user_address,              // 用户收货地址
            ) = self::preCheck($calc_type, $goods_list, $order_type, $groupon_id, $activity_id, $buy_type, $address_id, $hongbao, $quanyi);

        // 计算订单商品价格，所需积分，运费
        list(
            $new_goods_list,            // 新的商品列表
            $goods_original_amount,     // 商品原始总价
            $goods_amount,              // 商品现在总价
            $dispatch_amount,           // 订单运费（所有商品种最高的，后面还需根据活动再次计算）
            $score_amount,              // 订单所需支付积分
            $hongbao_amount,            // 订单所需支付兑换券
            $canuse_hongbao,            // 订单可用兑换券
            $quanyi_amount,            // 订单所需支付消费补贴
            $canuse_quanyi,            // 订单可用消费补贴
            ) = self::preCalcAmount($new_goods_list, $user_address, $order_type);

        // 记录原始运费
        $origin_dispatch_amount = $dispatch_amount;

        // 计算订单优惠折扣，优惠券,邮费
        list(
            $new_goods_list,                // 重新赋值活动， 商品上增加了 activity_type
            $activity_discount_infos,       // 每个活动包含的优惠信息
            $activity_discount_money,       // 促销活动优惠总金额
            $activity_type,                 // 全部参与的活动类型
            $dispatch_amount,               // 重新计算的运费
            $user_coupons,                  // 使用的优惠券信息
            $coupon_money                   // 优惠券优惠金额
            ) = self::preCalcDiscount(
            $goods_list,
            $new_goods_list,
            $activity_discounts,
            $activity_type,
            $goods_amount,
            $dispatch_amount,
            $user_address,
            $order_type,
            $coupons_id
        );

        // 计算订单总金额，需支付金额
        list(
            $new_goods_list,
            $total_amount,
            $discount_fee,
            $total_fee,
            $coupon_fee,
            $hongbao_amount,
            $quanyi_amount
            ) = self::preCalcOrder(
            $new_goods_list,
            $goods_amount,
            $dispatch_amount,
            $activity_discount_money,
            $coupon_money,
            $off_price,
            $hongbao_amount,
            $quanyi_amount
        );

        // 处理返回结果
        $result = self::preReturnParams(
            $goods_original_amount,
            $goods_amount,
            $origin_dispatch_amount,
            $dispatch_amount,
            $total_amount,
            $total_fee,
            $discount_fee,
            $coupon_fee,
            $activity_discount_money,
            $activity_type,
            $score_amount,
            $new_goods_list,
            $need_address,
            $activity_discount_infos,
            $user_coupons,
            $user_address,
            $calc_type,
            $hongbao_amount,
            $canuse_hongbao,
            $quanyi_amount,
            $canuse_quanyi
        );

        // 随机立减
        $delivery_card_id = $params['delivery_card_id'] ?? 0;
        if ($order_type == 'goods' && empty($delivery_card_id) && empty($activity_id) && empty($off_price)) {
            $offprice_config = config::get('order');
            if (!empty($offprice_config['offprice_enable'])) {
                $min = intval(($offprice_config['offprice_min'] ?? 0) * 100);
                $max = intval(($offprice_config['offprice_max'] ?? 0) * 100);
                $result['off'] = [$min, $max];
            }
        }

        return $result;
    }

    /**
     * 获取请求参数，初始化，并设置默认值
     *
     * @param array $params
     * @return array
     */
    public static function preParams($params) {
        $order_type = $params['order_type'] ?? 'goods';
        $groupon_id = $params['groupon_id'] ?? 0;        // 拼团的 团 id;
        $buy_type = $params['buy_type'] ?? 'alone';      // 拼团的 购买方式: alone=单独购买,groupon=开团;
        $goods_list = $params['goods_list'];
        $address_id = $params['address_id'];
        $coupons_id = $params['coupons_id'];
        $activity_id = $params['activity_id'] ?? 0;
        $from = $params['from'];
        $off_price = $params['off_price'] ?? 0;
        $hongbao = $params['hongbao'] ?? 1;
        $quanyi = $params['quanyi'] ?? 1;
        return [
            $goods_list,
            $order_type,
            $groupon_id,
            $buy_type,
            $address_id,
            $coupons_id,
            $activity_id,
            $from,
            $off_price,
            $hongbao,
            $quanyi
        ];
    }

    /**
     * 下单前检测，商品状态，秒杀，拼团活动状态，必要的选择项（比如下单收货地址），收货地址，兑换券等
     *
     * @param array $params，请求参数
     * @return array
     */
    public static function preCheck($calc_type, $goods_list, $order_type, $groupon_id, $activity_id, $buy_type, $address_id, $hongbao, $quanyi)
    {
        $user = auth::userinfo();

        $new_goods_list = [];
        $activity_type = '';
        $activity_discounts = [];
        $hongbao_max = $user['hongbao'];
        $quanyi_max = $user['quanyi'];
        foreach ($goods_list as $key => $buyinfo) {
            // 最少购买一件
            $buyinfo['goods_num'] = intval($buyinfo['goods_num']) < 1 ? 1 : intval($buyinfo['goods_num']);
            $sku_price_id = $buyinfo['sku_price_id'];

            // 查询商品
            $detail = \goodsModel::getGoodsDetail($buyinfo['goods_id']);
            $detail['hongbao_discounts'] = 0;
            $detail['canuse_hongbao'] = 0;
            $detail['quanyi_discounts'] = 0;
            $detail['canuse_quanyi'] = 0;
            if (!$detail || ($order_type != 'score' && $detail['status'] === 'down')) {
                throw new \Exception('商品不存在或已下架');
            }

            // 积分商城能量商品（不支持加购物车只可单买）
            if ($order_type == 'score') {
                $score_goods_sku_price = scoregoodsModel::where('sku_price_id', $sku_price_id)->first();
                $detail['current_sku_price']['price'] = $score_goods_sku_price->price; // 使用积分商城设置的sku_price
                $detail['current_sku_price']['score'] = $score_goods_sku_price->score;
                $detail['current_sku_price']['stock'] = $score_goods_sku_price->stock;
            }

            // 商城订单，活动商品（活动指的就是秒杀，无其他活动。秒杀不支持加购物车只可单买）
            if ($order_type == 'goods' && !empty($activity_id)) {
                $activity_type = 'seckill';

                // 查询活动
                $time = time();
                $activity = DB::table('activity')->find($activity_id);
                if (!($activity->status == 1 && $activity->starttime < $time && $activity->endtime > $time)) {
                    throw new \Exception('秒杀活动状态异常');
                }

                // 检测活动商品是否有库存
                $kucun_key = "activity_{$activity_id}_goods_{$detail['id']}_skupriceid_{$sku_price_id}";
                $kucun_val = RedisService::llen($kucun_key);

                $activity_goods_info_key = "activity_{$activity_id}_goods_{$detail['id']}";
                $activity_goods_sku_prices = RedisService::hget($activity_goods_info_key, 'sku_price');
                $activity_goods_sku_prices = json_decode($activity_goods_sku_prices, true);
                if (empty($activity_goods_sku_prices)) {
                    throw new \Exception('活动商品规格异常');
                }

                // 使用秒杀活动设置的sku_price
                foreach ($activity_goods_sku_prices as $activity_goods_sku_price) {
                    if ($activity_goods_sku_price['id'] == $sku_price_id) {
                        $detail['current_sku_price']['price'] = $activity_goods_sku_price['price'];
                    }
                }
                $detail['current_sku_price']['stock'] = $kucun_val;
            }

            $sku_price = \goodsskupriceModel::find($sku_price_id);
            if (empty($sku_price)) {
                throw new \Exception('商品规格不存在');
            }

            $origin_goods_sku_text = $sku_price->getRawOriginal('goods_sku_text');
            $sku_price = $sku_price->toArray();
            $sku_price['goods_sku_text'] = $origin_goods_sku_text;
            $detail['current_sku_price'] = array_merge($sku_price, $detail['current_sku_price'] ?? []);

            if (empty($detail['current_sku_price']['stock'])) {
                throw new \Exception('商品库存不足');
            }

            // 判断商品是否选择了配送方式
            if (!isset($buyinfo['dispatch_type']) || empty($buyinfo['dispatch_type'])) {
                // 不存在，或者为空，默认获第一个
                $current_dispatch_type = array_filter(explode(',', $detail['dispatch_type']));
                $buyinfo['dispatch_type'] = $current_dispatch_type[0] ?? '';
            }

            if ($calc_type == 'create') {
                if (empty($buyinfo['dispatch_type'])) {
                    throw new \Exception("请选择配送方式");
                }
                if ($buyinfo['dispatch_type'] == 'selfetch' && (!isset($buyinfo['store_id']) || !$buyinfo['store_id'])) {
                    throw new \Exception("请选择自提点");
                }
            }
            if($order_type == 'hongbao' && empty($activity_type) && $hongbao_max > 0 && $detail['enable_hongbao'] > 0){
                $current_goods_total_price = bcmul($detail['current_sku_price']['price'], $buyinfo['goods_num'], 2);
                if (bccomp($hongbao_max, $current_goods_total_price, 2) >= 0){
                    $hongbao_max = bcsub($hongbao_max, $current_goods_total_price, 2);
                    $detail['canuse_hongbao'] = $current_goods_total_price;
                }else{
                    $detail['canuse_hongbao'] = $hongbao_max;
                    $hongbao_max = 0;
                }
                if($hongbao>0){
                    $detail['hongbao_discounts'] = $detail['canuse_hongbao'];
                }
            }

            if($order_type == 'quanyi' && empty($activity_type) && $quanyi_max > 0 && $detail['enable_quanyi'] > 0){
                $current_goods_total_price = bcmul($detail['current_sku_price']['price'], $buyinfo['goods_num'], 2);
                if (bccomp($quanyi_max, $current_goods_total_price, 2) >= 0){
                    $quanyi_max = bcsub($quanyi_max, $current_goods_total_price, 2);
                    $detail['canuse_quanyi'] = $current_goods_total_price;
                }else{
                    $detail['canuse_quanyi'] = $quanyi_max;
                    $quanyi_max = 0;
                }
                if($quanyi>0){
                    $detail['quanyi_discounts'] = $detail['canuse_quanyi'];
                }
            }

            // 组装 商品详情
            $buyinfo['activity_type'] = $activity_type;
            $buyinfo['detail'] = $detail;
            $new_goods_list[] = $buyinfo;
        }

        $need_address = 1;
        $user_address = null;
        // 判断是否有需要收货地址的商品，新版每个商品需要选择配送方式
        $dispatchTypes = array_column($new_goods_list, 'dispatch_type');
        if (in_array('express', $dispatchTypes) || in_array('store', $dispatchTypes)) {
            // 配送方式为 快递 或者 自提 必须填写收货地址
            $user_address = \memberaddressModel::where("member_id", $user['id'])->find($address_id);
            if (empty($user_address) && $calc_type == 'create') {
                throw new \Exception("请选择正确的收货地址");
            }
        } else {
            // 不需要收货地址
            $need_address = 0;
        }

        return [
            $new_goods_list,
            $activity_type,
            $activity_discounts,
            $need_address,
            $user_address
        ];
    }


    /**
     * 计算订单各种费用
     *
     * @param array $new_goods_list
     * @param array $user_address
     * @return array
     */
    public static function preCalcAmount($new_goods_list, $user_address, $order_type) {

        $goods_original_amount = 0;         // 商品原始总价
        $goods_amount = 0;                  // 商品总价
        $dispatch_amount = 0;               // 运费总价
        $score_amount = 0;                  // 订单总积分
        $hongbao_amount = 0;                // 兑换券支付金额
        $canuse_hongbao = 0;                // 可用兑换券支付金额
        $quanyi_amount = 0;                // 消费补贴支付金额
        $canuse_quanyi = 0;                // 可用消费补贴支付金额
        // 计算商品金额
        foreach ($new_goods_list as $key => $buyinfo) {
            $detail = $buyinfo['detail'];

            // 当前商品原始总价
            $current_goods_original_amount = bcmul($detail['original_price'], $buyinfo['goods_num'], 2);
            $goods_original_amount = bcadd($goods_original_amount, $current_goods_original_amount, 2);

            // 当前商品现在总价
            $current_goods_amount = bcmul($detail['current_sku_price']['price'], $buyinfo['goods_num'], 2);
            $goods_amount = bcadd($goods_amount, $current_goods_amount, 2);

            // 如果商家配送，默认选择最近的门店
            $current_store_id = $buyinfo['store_id'] ?? 0;

            // 获取配送数据
            if ($buyinfo['dispatch_type']) {
                // TODO 发货设置
                $dispatchData = \dispatchModel::getDispatch($buyinfo['dispatch_type'], $detail, [
                    'address' => $user_address,
                    'goods_num' => $buyinfo['goods_num'],
                ]);
            }

            // 配送模板 id
            $current_dispatch_id = $dispatchData['dispatch_id'] ?? 0;
            // 配送费
            $current_dispatch_amount = $dispatchData['dispatch_amount'] ?? 0;

            // TODO 计算运费
            // if (
            //     $buyinfo['dispatch_type'] == 'store'
            //     && isset($dispatchData['store'])
            //     && $dispatchData['store']
            // ) {
            //     // 商家配送,自动采用最近的 store
            //     $current_store_id = $dispatchData['store']['id'];
            // }
            //
            // // 选用商品中最高运费，暂时，下面会根据包邮活动的结果，再计算一次
            // $bcres = bccomp($dispatch_amount, $current_dispatch_amount, 2);     // 比较两个运费
            // $dispatch_amount = ($bcres == 1 || $bcres == 0) ? $dispatch_amount : $current_dispatch_amount;      // 取用最大的

            // 当前商品所需积分
            $current_score_amount = 0;
            if ($order_type == 'score') {       // 积分商城规格
                $current_score_amount = ($detail['current_sku_price']['score'] * $buyinfo['goods_num']);
                $score_amount += $current_score_amount;
            }

            // 当前商品所需兑换券
            if ($order_type == 'hongbao') {       // 兑换券累计
                $hongbao_amount = bcadd($hongbao_amount, $detail['hongbao_discounts'], 2);
                $canuse_hongbao = bcadd($canuse_hongbao, $detail['canuse_hongbao'], 2);;
            }

            // 当前商品所需消费补贴
            if ($order_type == 'quanyi') {       // 兑换券累计
                $quanyi_amount = bcadd($quanyi_amount, $detail['quanyi_discounts'], 2);
                $canuse_quanyi = bcadd($canuse_quanyi, $detail['canuse_quanyi'], 2);;
            }

            // 将计算好的属性记录下来，插入订单 item 表使用
            $new_goods_list[$key]['goods_original_amount'] = $current_goods_original_amount;
            $new_goods_list[$key]['goods_amount'] = $current_goods_amount;
            $new_goods_list[$key]['dispatch_amount'] = $current_dispatch_amount;
            $new_goods_list[$key]['score_amount'] = $current_score_amount;
            $new_goods_list[$key]['hongbao_amount'] = $detail['hongbao_discounts'];
            $new_goods_list[$key]['quanyi_amount'] = $detail['quanyi_discounts'];
            $new_goods_list[$key]['dispatch_id'] = $current_dispatch_id;
            $new_goods_list[$key]['store_id'] = $current_store_id;
            $dispatch_amount = bcadd($dispatch_amount, $current_dispatch_amount, 2);
        }

        return [
            $new_goods_list,                // 新的商品列表
            $goods_original_amount,         // 商品原始总价
            $goods_amount,                  // 商品总价
            $dispatch_amount,               // 运费总价
            $score_amount,                  // 订单总积分
            $hongbao_amount,                 // 订单总兑换券
            $canuse_hongbao,
            $quanyi_amount,                 // 订单总消费补贴金额
            $canuse_quanyi                  // 订单最多可用消费补贴金额
        ];
    }


    /**
     * 计算商品的优惠，优惠促销 和 优惠券
     *
     * @param array $new_goods_list
     * @param array $activity_discounts
     * @param string $activity_type
     * @param string $dispatch_amount
     * @param array $user_address   用户收货地址
     * @return array
     */
    public static function preCalcDiscount(
        $goods_list,
        $new_goods_list,
        $activity_discounts,
        $activity_type,
        $goods_amount,
        $dispatch_amount,
        $user_address,
        $order_type,
        $coupons_id
    ) {

        $activity_discount_infos = [];      // 参与的所有促销活动信息
        $activity_discount_money = 0;       // 促销活动金额

        $discounts = [];
        // 过滤重复活动
        foreach ($activity_discounts as $activity_discount) {
            if (!isset($discounts[$activity_discount['id']])){
                $discounts[$activity_discount['id']] = $activity_discount;
            }
        }

        // 将购买的商品，按照活动分类
        foreach ($new_goods_list as $new_goods) {
            $detail = $new_goods['detail'];
            unset($new_goods['detail']);
            if (isset($detail['activity_discounts']) && $detail['activity_discounts']) {
                foreach ($detail['activity_discounts'] as $ad) {
                    $discounts[$ad['id']]['goods'][] = $new_goods;
                }
            }
        }

        // 计算各个活动是否满足
        $freeShippingGoodsIds = [];
        foreach($discounts as $key => $discount) {
            if (!isset($discount['goods'])) {
                // 活动没有商品，直接 next
                continue;
            }

            $discount_total_money = 0;          // 该活动中商品的总价
            $discount_total_num = 0;            // 该活动商品总件数
            $goodsIds = [];                     // 该活动中所有的商品 id
            $current_discount_dispatch_amount = 0;  // 该活动中最高运费

            // 活动中的商品总金额，总件数，所有商品 id
            foreach ($discount['goods'] as $goods) {
                $discount_total_money += $goods['goods_amount'];
                $discount_total_num += $goods['goods_num'];
                $goodsIds[] = $goods['goods_id'];

                $bcres = bccomp($current_discount_dispatch_amount, $goods['dispatch_amount'], 2);     // 比较两个运费
                $current_discount_dispatch_amount = ($bcres == 1 || $bcres == 0) ? $current_discount_dispatch_amount : $goods['dispatch_amount'];      // 取用最大的
            }

            $rules = $discount['rules'];
            // 是按金额，还是按件数比较
            $compareif = $rules['type'] == 'num' ? 'discount_total_num' : 'discount_total_money';

            if (in_array($discount['type'], ['full_reduce', 'full_discount'])) {
                // 将规则按照从大到校排列,优先比较是否满足最大规则
                $rules_discounts = isset($rules['discounts']) && $rules['discounts'] ? array_reverse($rules['discounts']) : [];    // 数组反转

                // 满减， 满折多个规则从大到小匹配最优惠
                foreach ($rules_discounts as $d) {
                    if (${$compareif} < $d['full']) {
                        // 不满足条件，接着循环下个规则
                        continue;
                    }

                    // 满足优惠
                    if ($discount['type'] == 'full_reduce') {
                        $current_activity_discount_money = (isset($d['discount']) && $d['discount']) ? $d['discount'] : 0;
                    } else {
                        $dis = bcdiv($d['discount'], 10, 3);        // 保留三位小数，转化折扣
                        $dis = $dis > 1 ? 1 : ($dis < 0 ? 0 : $dis);    // 定义边界 0 - 1
                        $current_activity_discount_money = bcmul($discount_total_money, $dis, 2);       // 计算折扣金额
                    }

                    // 记录该活动的一些统计信息
                    $activity_discount_infos[] = [
                        'activity_id' => $discount['id'],                           // 活动id
                        'activity_title' => $discount['title'],                     // 活动标题
                        'activity_type' => $discount['type'],                       // 活动类型
                        'activity_discount_money' => $current_activity_discount_money,      // 优惠金额
                        'rule_type' => $rules['type'],                              // 满多少元|还是满多少件
                        'discount_rule' => $d,                                      // 满足的那条规则
                        'goods_ids' => join(',', $goodsIds)                         // 这个活动包含的这次购买的商品
                    ];

                    // 累加促销活动总计优惠金额
                    $activity_discount_money += $current_activity_discount_money;

                    // 拼接参与的活动类型
                    $activity_type .= $discount['type'] . ',';
                    break;
                }
            } else if ($discount['type'] == 'free_shipping') {
                // 判断除外的地区
                $area_except = $rules['area_except'] ?? '';
                $city_except = $rules['city_except'] ?? '';
                $province_except = $rules['province_except'] ?? '';
                if ($user_address) {
                    if (strpos($area_except, strval($user_address['area_id'])) !== false
                        || strpos($city_except, strval($user_address['city_id'])) !== false
                        || strpos($province_except, strval($user_address['province_id'])) !== false
                    ) {
                        // 收货地址在非包邮地区，则继续循环下个活动
                        continue;
                    }
                } else if ($area_except || $city_except || $province_except) {
                    // 没有选择收货地址，并且活动中包含地区限制,不计算活动
                    continue;
                }

                if (${$compareif} < $rules['full_num']) {
                    // 不满足条件，接着循环下个规则
                    continue;
                }

                // 记录活动信息
                $activity_discount_infos[] = [
                    'activity_id' => $discount['id'],                                   // 活动id
                    'activity_title' => $discount['title'],                             // 活动标题
                    'activity_type' => $discount['type'],                               // 活动类型
                    'activity_discount_money' => $current_discount_dispatch_amount,     // 活动减免的运费
                    'rule_type' => $rules['type'],                                      // 满多少元|还是满多少件
                    'discount_rule' => [
                        'full_num' => $rules['full_num']
                    ],                                                                  // 满足的那条规则
                    'goods_ids' => join(',', $goodsIds)                                 // 这个活动包含的这次购买的商品
                ];

                // 免运费的活动的id
                $freeShippingGoodsIds = array_merge($freeShippingGoodsIds, $goodsIds);

                // 拼接参与的活动类型
                $activity_type .= $discount['type'] . ',';
            }
        }

        // 多活动拼接去掉多余的 ,
        $activity_type = rtrim($activity_type, ',');

        // 重新计算运费
        if ($freeShippingGoodsIds) {
            // 存在免运费的商品
            $new_dispatch_amount = 0;
            foreach ($new_goods_list as $goods) {
                if (!in_array($goods['goods_id'], $freeShippingGoodsIds)) {
                    // 如果这个商品不在包邮活动中,计算运费
                    $bcres = bccomp($new_dispatch_amount, $goods['dispatch_amount'], 2);     // 比较两个运费
                    $new_dispatch_amount = ($bcres == 1 || $bcres == 0) ? $new_dispatch_amount : $goods['dispatch_amount'];      // 取用最大的
                }
            }

            if ($new_dispatch_amount < $dispatch_amount) {
                // 邮费省钱了
                $activity_discount_money += ($dispatch_amount - $new_dispatch_amount);
                // $dispatch_amount = $new_dispatch_amount;        // 重新赋值运费，新计算的运费，不可能比 dispatch_amount 大（（搜这个找注释）ps: 这里还展示真实的运费，因为优惠总金额已经加上了优惠的运费）
            }
        }

        // 计算优惠券费用
        $user_coupons = null;
        $coupon_money = 0;
        if ($coupons_id) {
            if (strpos($activity_type, 'seckill') !== false || strpos($activity_type, 'groupon') !== false) {
                // 拼团或者秒杀
                throw new \Exception('活动商品不可使用优惠券');
            }

            if ($order_type == 'score') {
                throw new \Exception('积分商品不可使用优惠券');
            }

            // 查询传来的优惠券 id 是否可用
            $coupons = self::coupons($goods_list, $goods_amount);

            $current_coupons = null;        // 当前所选优惠券
            foreach ($coupons as $key => $coupon) {
                if ($coupon['user_coupons_id'] == $coupons_id) {
                    $current_coupons = $coupon;
                    break;
                }
            }

            if ($current_coupons) {
                $coupon_money = $current_coupons->amount;     // 金额在 coupons 表存着
                $user_coupons = \membercouponModel::where('id', $coupons_id)->first();        // 用户优惠券
            } else {
                throw new \Exception('优惠券不可用');
            }
        }

        if ($activity_discount_infos) {
            // 将每个商品对应的 activity_type 放入 new_goods_list
            $new_goods_list = array_column($new_goods_list, null, 'goods_id');

            foreach($activity_discount_infos as $info) {
                $goodsIds = explode(',', $info['goods_ids']);

                foreach ($goodsIds as $goods_id) {
                    $new_goods_list[$goods_id]['activity_type'] .= $info['activity_type'] . ',';
                }
            }

            // 去除多余的 ,
            foreach($new_goods_list as $key => $goods) {
                $new_goods_list[$key]['activity_type'] = rtrim($new_goods_list[$key]['activity_type'], ',');
            }

            $new_goods_list = array_values($new_goods_list);
        }

        return [
            $new_goods_list,
            $activity_discount_infos,
            $activity_discount_money,
            $activity_type,
            $dispatch_amount,
            $user_coupons,
            $coupon_money
        ];
    }


    /**
     * 计算订单费用
     *
     * @param array $new_goods_list
     * @param float $goods_amount
     * @param float $origin_dispatch_amount 原始运费
     * @param float $dispatch_amount
     * @param int $score_amount
     * @param float $activity_discount_money
     * @param float $coupon_money
     * @return array
     */
    public static function preCalcOrder(
        $new_goods_list,
        $goods_amount,
        $dispatch_amount,
        $activity_discount_money,
        $coupon_money,
        $offprice,
        $hongbao_money,
        $quanyi_money
    ) {
        // （搜这个找注释）加上运费，dispatch_amount 没有重新计算，和 origin_dispatch_amount 相同
        $total_amount = bcadd($goods_amount, $dispatch_amount, 2);
        $coupon_fee = $coupon_money;
        $discount_fee = bcadd($coupon_money, $activity_discount_money, 2);
        $discount_fee = bcadd($discount_fee, $offprice, 2);


        // 重新计算每个商品分配到的优惠
        $discount_fee_surplus = $discount_fee;
        $most_dear_goods_item_key = 0;
        foreach ($new_goods_list as $key => $buyinfo) {
            if ($key > 0 && $buyinfo['goods_amount'] > $new_goods_list[$most_dear_goods_item_key]['goods_amount']) {
                $most_dear_goods_item_key = $key;
            }
            $scale = 0;                             // 按照商品价格和总价计算每个 item 的比例
            if (floatval($goods_amount)) {          // 字符串 0.00 是 true, 这里转下类型在判断
                $scale = bcdiv($buyinfo['goods_amount'], $goods_amount, 6);
            }

            // 每个商品分配到的折扣
            $current_discount_fee = bcmul($discount_fee, $scale, 2);
            $new_goods_list[$key]['discount_fee'] = $current_discount_fee;
            // 每个商品除了运费之后分配的支付金额
            $new_goods_list[$key]['pay_price'] = bcsub($buyinfo['goods_amount'], $current_discount_fee, 2);
            // 每个商品使用兑换券后的支付金额
            $new_goods_list[$key]['pay_price'] = bcsub($new_goods_list[$key]['pay_price'], $buyinfo['hongbao_amount'], 2);
            // 每个商品使用消费补贴后的支付金额
            $new_goods_list[$key]['pay_price'] = bcsub($new_goods_list[$key]['pay_price'], $buyinfo['quanyi_amount'], 2);
            $discount_fee_surplus = bcsub($discount_fee_surplus, $current_discount_fee, 2);
        }
        if ($discount_fee_surplus > 0) {
            foreach ($new_goods_list as $key => $value) {
                if ($key == $most_dear_goods_item_key) {
                    $new_goods_list[$key]['discount_fee'] = bcadd($value['discount_fee'], $discount_fee_surplus, 2);
                    $new_goods_list[$key]['pay_price'] = bcsub($value['pay_price'], $discount_fee_surplus, 2);
                }
            }
        }
        //兑换券支付的不平均计算到每个商品内
        $discount_fee = bcadd($discount_fee, $hongbao_money, 2);
        //消费补贴支付的不平均计算到每个商品内
        $discount_fee = bcadd($discount_fee, $quanyi_money, 2);

        $total_fee = bcsub($total_amount, $discount_fee, 2);
        $total_fee = $total_fee < 0 ? 0 : $total_fee;
        return [
            $new_goods_list,
            $total_amount,
            $discount_fee,
            $total_fee,
            $coupon_fee,
            $hongbao_money,
            $quanyi_money,
        ];
    }

    /**
     * 处理返回结果
     *
     * @param float $goods_original_amount
     * @param float $goods_amount
     * @param float $dispatch_amount
     * @param float $total_amount
     * @param float $total_fee
     * @param float $discount_fee
     * @param float $coupon_fee
     * @param float $activity_discount_money
     * @param string $activity_type
     * @param int $score_amount
     * @param array $new_goods_list
     * @param array $need_address
     * @param array $activity_discount_infos
     * @param array $user_coupons
     * @param array $user_address
     * @param string $calc_type
     * @param float $hongbao_amount
     * @param float $quanyi_amount
     * @return array
     */
    public static function preReturnParams(
        $goods_original_amount,
        $goods_amount,
        $origin_dispatch_amount,
        $dispatch_amount,
        $total_amount,
        $total_fee,
        $discount_fee,
        $coupon_fee,
        $activity_discount_money,
        $activity_type,
        $score_amount,
        $new_goods_list,
        $need_address,
        $activity_discount_infos,
        $user_coupons,
        $user_address,
        $calc_type,      // 计算方式
        $hongbao_amount,
        $canuse_hongbao,
        $quanyi_amount,
        $canuse_quanyi
    ) {
        // 需要处理小数点的数据
        $result = compact(
            "goods_original_amount",
            "goods_amount",
            "origin_dispatch_amount",
            "dispatch_amount",
            "total_amount",
            "total_fee",
            "discount_fee",
            "coupon_fee",
            "activity_discount_money",
            "hongbao_amount",
            "canuse_hongbao",
            "quanyi_amount",
            "canuse_quanyi"
        );

        // 处理小数点保留两位小数
        foreach ($result as $key => $amount) {
            $result[$key] = number_format($amount, 2, '.', '');
        }

        // 合并不需要处理小数点的
        $result = array_merge($result, compact(
            "activity_type",
            "score_amount",
            "new_goods_list",
            "need_address",
            "activity_discount_infos"
        ));

        // 如果是下单，合并 优惠券， 收货地址
        if ($calc_type == 'create') {
            $result = array_merge($result, compact(
                "user_coupons",
                "user_address"
            ));
        }

        return $result;
    }

    /************************************* 订单预览 stop *************************************/




    /************************************* 订单创建 start *************************************/

    public static function createOrder($params)
    {
        $from = $params['from'];
        $order_type = $params['order_type'] ?? 'goods';
        $buy_type = $params['buy_type'] ?? 'alone';      // 购买方式: alone=单独购买,groupon=开团;
        $groupon_id = $params['groupon_id'] ?? 0;        // 拼团 id;
        $activity_id = $params['activity_id'] ?? 0;      // 秒杀活动 id;
        $remark = $params['remark'] ?? '';
        $off_price = $params['off_price'] ?? 0;
        $customer_id = $params['customer_id']  ?? 0;
        $delivery_card_id = $params['delivery_card_id'] ?? 0;
        $hongbao_amount = $params['hongbao_amount']  ?? 0;
        $quanyi_amount = $params['quanyi_amount']  ?? 0;
        // 订单计算数据
        $preData = self::previewOrder($params, "create");

        // 订单价格异常
        if (floatval($preData['total_fee']) < 0) {
            return false;
        }
        if (!empty($preData['new_goods_list'])) {
            foreach ($preData['new_goods_list'] as $buyinfo) {
                if (floatval($buyinfo['pay_price']) < 0) {
                    return false;
                }
            }
        }

        return Db::transaction(function () use (
            $from,
            $order_type,
            $buy_type,
            $groupon_id,
            $activity_id,
            $remark,
            $off_price,
            $preData,
            $customer_id,
            $delivery_card_id
        ) {
            $userinfo = auth::userinfo();
            $user = \memberModel::find($userinfo['id']);

            if(bccomp($preData['hongbao_amount'], 0, 2) > 0 && bccomp($user->hongbao, $preData['hongbao_amount'], 2) < 0){
                throw new \Exception('账户兑换券不足');
            }
            if(bccomp($preData['quanyi_amount'], 0, 2) > 0 && bccomp($user->quanyi, $preData['quanyi_amount'], 2) < 0){
                throw new \Exception('账户消费补贴不足');
            }

            $goods_original_amount = $preData['goods_original_amount'];
            $goods_amount = $preData['goods_amount'];
            $dispatch_amount = $preData['dispatch_amount'];
            $total_amount = $preData['total_amount'];
            $hongbao_amount = $preData['hongbao_amount'];
            $quanyi_amount = $preData['quanyi_amount'];
            $score_amount = $preData['score_amount'];
            $total_fee = $preData['total_fee'];
            //折扣金额需要扣除 兑换券支付金额
            $discount_fee = bcsub($preData['discount_fee'], $preData['hongbao_amount'], 2);
            //折扣金额需要扣除 消费补贴支付金额
            $discount_fee = bcsub($preData['discount_fee'], $preData['quanyi_amount'], 2);
            $coupon_fee = $preData['coupon_fee'];
            $activity_discount_money = $preData['activity_discount_money'];
            $activity_discount_infos = $preData['activity_discount_infos'];
            $new_goods_list = $preData['new_goods_list'];
            $activity_type = $preData['activity_type'];
            $user_coupons = $preData['user_coupons'];
            $user_address = $preData['user_address'];

            $orderData = [];
            $orderData['order_sn'] = self::generateSn($user->id);
            $orderData['member_id'] = $user->id;
            $orderData['customer_id'] = $customer_id;
            $orderData['type'] = ($order_type!='score')?'goods':'score';
            $orderData['activity_type'] = $activity_type;
            $orderData['goods_amount'] = $goods_amount;
            $orderData['dispatch_amount'] = $dispatch_amount;
            $orderData['total_amount'] = $total_amount;
            $orderData['score_amount'] = $score_amount;
            $orderData['hongbao_amount'] = $hongbao_amount;
            $orderData['quanyi_amount'] = $quanyi_amount;
            $orderData['total_fee'] = $total_fee;
            $orderData['pay_fee'] = $total_fee;
            $orderData['discount_fee'] = $discount_fee;
            $orderData['score_fee'] = $score_amount;          // 记录score 支付数
            $orderData['hongbao_fee'] = $hongbao_amount;          // 记录兑换券 支付数
            $orderData['quanyi_fee'] = $quanyi_amount;          // 记录消费补贴 支付数
            $orderData['coupon_fee'] = $coupon_fee;
            $orderData['activity_discount_money'] = $activity_discount_money;
            $orderData['goods_original_amount'] = $goods_original_amount;
            $orderData['off_price'] = $off_price;

            if ($user_address) {
                $orderData['phone'] = $user_address->phone;
                $orderData['consignee'] = $user_address->consignee;
                $orderData['province_name'] = $user_address->province_name;
                $orderData['city_name'] = $user_address->city_name;
                $orderData['area_name'] = $user_address->area_name;
                $orderData['address'] = $user_address->address;
                $orderData['province_id'] = $user_address->province_id;
                $orderData['city_id'] = $user_address->city_id;
                $orderData['area_id'] = $user_address->area_id;
            }
            $orderData['status'] = 0;
            $orderData['remark'] = $remark;
            $orderData['coupons_id'] = $user_coupons ? $user_coupons->id : 0;
            $orderData['platform'] = getallheaders()['Platform'] ?? '';

            // 促销、优惠券、积分

            $ext = $activity_discount_infos ? ['activity_discount_infos' => $activity_discount_infos] : []; // 促销活动信息
            if ($delivery_card_id) {
                $ext['pickgoods_card_id'] = $delivery_card_id;
            }
            $orderData['ext'] = json_encode($ext);

            // // 将优惠券使用掉
            // if ($user_coupons) {
            //     $user_coupons->use_order_id = $order->id;
            //     $user_coupons->usetime = time();
            //     $user_coupons->save();
            // }

            // 添加 订单 item
            foreach ($new_goods_list as $key => $buyinfo) {
                $goods_id = $buyinfo['goods_id'];
                $goods_sku_price_id = $buyinfo['sku_price_id'];
                $detail = $buyinfo['detail'];
                $current_sku_price = $detail['current_sku_price'];

                // 抢购秒杀商品
                if ($activity_id) {
                    $kucun_key = "activity_{$activity_id}_goods_{$goods_id}_skupriceid_{$goods_sku_price_id}";
                    $seckill = RedisService::rpop($kucun_key);
                    if (!$seckill) {
                        throw new \Exception('手慢了');
                    }
                }

                // 积分商品所需积分
                if ($order_type == 'score' && $user->score < $current_sku_price['score']) {
                    throw new \Exception('账户积分不足');
                }

                $orderItem = new \orderitemModel();

                $orderItem->member_id = $customer_id > 0 ? $customer_id : $user->id;
                $orderItem->goods_id = $buyinfo['goods_id'];
                $orderItem->goods_title = $detail['title'];
                $orderItem->goods_type = $detail['type'];
                $orderItem->goods_sku_price_id = $buyinfo['sku_price_id'];
                $orderItem->goods_sku_text = $current_sku_price['goods_sku_text'];
                $orderItem->calc_consume = $detail['calc_consume'];

                $orderItem->activity_id = $activity_id;
                $orderItem->activity_type = $activity_type;

                $orderItem->goods_image = empty($current_sku_price['image']) ? $detail['image'] : $current_sku_price['image'];
                $orderItem->goods_original_price = $detail['original_price'];
                $orderItem->cost_price = $current_sku_price['cost_price'];
                $orderItem->discount_fee = $buyinfo['discount_fee'];        // 平均计算单件商品所享受的折扣
                $orderItem->hongbao_fee = $buyinfo['hongbao_amount'];        // 支付兑换券金额
                $orderItem->quanyi_fee = $buyinfo['quanyi_amount'];        // 支付消费补贴金额
                $orderItem->pay_price = $buyinfo['pay_price'];        // 平均计算单件商品不算运费，算折扣时候的金额
                $orderItem->goods_price = $detail['current_sku_price']['price'];
                $orderItem->goods_num = $buyinfo['goods_num'] ?? 1;
                $orderItem->goods_weight = $detail['current_sku_price']['weight'] ?: 0;
                $orderItem->dispatch_status = 0;
                $orderItem->dispatch_fee = $buyinfo['dispatch_amount'];
                $orderItem->dispatch_type = $buyinfo['dispatch_type'];
                $orderItem->dispatch_id = $buyinfo['dispatch_id'] ? $buyinfo['dispatch_id'] : 0;
                $orderItem->store_id = $buyinfo['store_id'] ? $buyinfo['store_id'] : 0;
                $orderItem->aftersale_status = 0;
                $orderItem->comment_status = 0;
                $orderItem->refund_status = 0;

                // $ext = [];
                // if (isset($buyinfo['dispatch_date'])) {
                //     $ext['dispatch_date'] = $buyinfo['dispatch_date'];
                // }
                // if (isset($buyinfo['dispatch_phone'])) {
                //     $ext['dispatch_phone'] = $buyinfo['dispatch_phone'];
                // }
                // $orderItem->ext = json_encode($ext);
                // if (isset($buyinfo['goods_combo']) && $buyinfo['goods_combo']) {
                //     $orderItem->goods_combo = json_encode($buyinfo['goods_combo']);
                // }

                $orderItems[] = $orderItem;
            }

            $orderId = \orderModel::insertGetId($orderData);
            foreach ($orderItems as $orderItem) {
                $orderItem->order_id = $orderId;
                $orderItem->save();
            }

            // 订单提交成功，
            \Hook::emit('order_create_after', ['from'  => $from, 'orderId' => $orderId]);

            // 重新获取订单
            return self::find($orderId);
        });
    }

    /************************************* 订单创建 stop *************************************/




    /************************************* 订单详情 start *************************************/

    public static function detail($params)
    {
        $userinfo = auth::userinfo();
        $user = \memberModel::find($userinfo['id']);

        $order = (new self())
            ->with('item')
            ->whereRaw('(member_id=? OR customer_id=?)', [$user->id, $user->id])
            ->where(function ($query) use ($params) {
                if (isset($params['order_sn'])) {
                    $query->where('order_sn', $params['order_sn']);
                }
                if (isset($params['id'])) {
                    $query->where('id', $params['id']);
                }
            })
            ->first();

        if (!$order) {
            throw new \Exception('订单不存在');
        }

        // 处理未支付订单 item status_code
        $order = self::setOrderItemStatusByOrder($order);

        return $order;
    }

    /************************************* 订单详情 stop *************************************/




    /************************************* 订单支付 start *************************************/

    public function paymentProcess($order, $notify)
    {
        // 支付成功
        \Hook::emit('order_payed_after', ['order' => $order, 'notify' => $notify]);

        // 预发放佣金 && 计算业绩（个人业绩 && 团队业绩 && 累计分佣） && 判断等级
        \Hook::emit('commission_init', ['order' => $order]);

        // 根据代理结算event初始化结算任务
        \Hook::emit('commission_settle', ['order' => $order, 'event' => 'payed']);
    }

    /************************************* 订单支付 stop *************************************/




    /************************************* 订单取消 start *************************************/

    public static function operCancel($params)
    {
        $user = auth::getUser();

        $order = self::with('item')
            ->where('member_id', $user->id)
            ->where('id', $params['id'])
            ->nopay()
            ->first();

        if (!$order) {
            throw new \Exception('订单不存在或已取消');
        }

        // 订单取消
        $order = (new self)->doCancel($order, $user);

        return $order;
    }

    public function doCancel($order, $user, $type = 'user')
    {
        $order = Db::transaction(function () use ($order, $user, $type) {
            \Hook::emit('order_cancel_before', $order);

            $order->status = self::STATUS_CANCEL;        // 取消订单
            $order->ext = json_encode(array_merge($order->ext_arr, ['cancel_time' => time()]));      // 取消时间
            $order->save();

            \orderactionModel::operAdd($order, null, $user, $type, ($type == 'user' ? '用户' : '管理员') . '取消订单');

            // 订单取消，退回库存，退回优惠券积分，等
            \Hook::emit('order_cancel_after', $order);

            return $order;
        });

        return $order;
    }

    /************************************* 订单取消 stop *************************************/




    /************************************* 订单删除 start *************************************/

    public static function operDelete($params)
    {
        $user = auth::getUser();

        $order = self::with('item')
            ->where('member_id', $user->id)
            ->where('id', $params['id'])
            ->cancel()
            ->first();

        if (!$order) {
            throw new \Exception('订单不存在或已取消');
        }

        $order = Db::transaction(function () use ($order, $user) {
            $order->delete();        // 删除订单

            \orderactionModel::operAdd($order, null, $user, 'user', '用户删除订单');

            return $order;
        });

        return $order;
    }

    /************************************* 订单删除 stop *************************************/




    /************************************* 订单收货 start *************************************/

    private function getOper($oper_type, $origin_oper = null)
    {
        $oper = null;
        $oper_iden = '系统自动';
        if ($oper_type == 'system') {
            // 系统自动操作
            $oper = null;
            $oper_iden = '系统自动';
        } else if ($oper_type == 'user') {
            // 用户操作
            // $oper = $origin_oper ?: User::info();
            $oper = $origin_oper ?: null;
            $oper_iden = '用户';
        } else if ($oper_type == 'admin') {
            // 管理员操作
            $oper = $origin_oper ?: null;
            $oper_iden = '管理员';
        } else if ($oper_type == 'store') {
            // 门店管理员操作
            // $oper = $origin_oper ?: Store::info();
            $oper = $origin_oper ?: null;
            $oper_iden = '门店管理员';
        }

        return compact("oper", "oper_iden");
    }

    private static function getItem($order, $order_item_id)
    {
        if (!$order) {
            throw new \Exception('当前订单不存在');
        }

        $order_item = null;
        foreach ($order->item as $item) {
            if ($item->id == $order_item_id) {
                $order_item = $item;
                break;
            }
        }

        if (!$order_item) {
            throw new \Exception('订单商品不需要操作');
        }

        return $order_item;
    }

    public function getedItem($order, $item, $data = [])
    {
        $ext = ['confirm_time' => time()];
        if (isset($data['ext']) && $data['ext']) {
            $ext = array_merge($ext, $data['ext']);
        }
        $item->ext = json_encode($item->setExt($item, $ext));
        $item->dispatch_status = \orderitemModel::DISPATCH_STATUS_GETED;        // 确认收货
        $item->save();

        $oper_type = $data['oper_type'] ?? 'system';
        list($oper, $oper_iden) = $this->getOper($oper_type, $data['oper'] ?? null);
        \orderactionModel::operAdd($order, $item, $oper, $oper_type, $oper_iden . '确认收货');

        return $item;
    }

    public static function operConfirm($params)
    {
        $order = DB::transaction(function () use ($params) {
            $user = auth::getUser();
            // 加锁查询订单，exist 里面的子查询不会加锁，但是该语句需要等待锁释放才能正常查询，所以下面的获取 item 已经是更改过状态之后的了
            $order = self::noget()
                ->whereRaw('(member_id=? OR customer_id=?)', [$user->id, $user->id])
                ->where('id', $params['id'])
                ->lockForUpdate()
                ->first();

            // 获取要操作的 订单 item
            $item = self::getItem($order, $params['order_item_id']);
            if ($item->dispatch_status == \orderitemModel::DISPATCH_STATUS_NOSEND) {
                throw new \Exception('当前订单未发货');
            }
            if ($item->dispatch_status == \orderitemModel::DISPATCH_STATUS_GETED) {
                throw new \Exception('当前订单已收货');
            }

            $item = (new self())->getedItem($order, $item, ['oper_type' => 'user']);

            // 订单确认收货后
            \hook::emit('order_confirm_after', ['order' => $order, 'item' => $item]);
        });

        return $order;
    }

    public static function operConfirmAll($params)
    {
        $order = Db::transaction(function () use ($params) {
            $user = auth::getUser();
            // 加锁查询订单，exist 里面的子查询不会加锁，但是该语句需要等待锁释放才能正常查询，所以下面的获取 item 已经是更改过状态之后的了
            $order = self::noget()
                ->whereRaw('(member_id=? OR customer_id=?)', [$user->id, $user->id])
                ->where('id', $params['id'])
                ->lockForUpdate()
                ->first();

            $items = \orderitemModel::where('order_id', $order->id)->get();
            foreach ($items as $order_item) {

                // 获取要操作的 订单 item
                $item = self::getItem($order, $order_item['id']);
                if ($item->dispatch_status == \orderitemModel::DISPATCH_STATUS_NOSEND) {
                    throw new \Exception('当前订单未发货');
                }
                if ($item->dispatch_status == \orderitemModel::DISPATCH_STATUS_GETED) {
                    throw new \Exception('当前订单已收货');
                }

                $item = (new self())->getedItem($order, $item, ['oper_type' => 'user']);

                // 订单确认收货后
                \hook::emit('order_confirm_after', ['order' => $order, 'item' => $item]);
            }
        });

        return $order;
    }

    /************************************* 订单收货 stop *************************************/




    /************************************* 订单评价 start *************************************/

    public static function operComment($params)
    {
        $user = auth::getUser();

        $order = DB::transaction(function () use ($user, $params) {
            extract($params);

            // 加锁读取订单，直到订单评价状态改变
            $order = self::with('item')
                ->payed()
                ->where('member_id', $user->id)
                ->where('id', $params['id'])
                ->lockForUpdate()
                ->first();

            // 获取要操作的 订单 item
            $item = self::getItem($order, $params['order_item_id']);
            $parent_id = 0;
            $comment_status = 'show';
            if ($item->comment_status == 1) {
                $parent_comment = \goodscommentModel::query()->where([
                    ['parent_id', '=', 0],
                    ['goods_id', '=', $item->goods_id],
                    ['order_id', '=', $order->id],
                    ['order_item_id', '=', $item->id],
                    ['member_id', '=', $user->id]])->limit(1)->get()->toArray();
                if($parent_comment){
                    $parent_id = $parent_comment[0]['id'];
                    $comment_status = $parent_comment[0]['status'];
                }
            }

            // 订单评价前
            \Hook::emit('order_comment_before', ['order' => $order, 'item' => $item]); // 重新拿到更新过的订单

            $images = (!empty($params['images']) && is_array($params['images'])) ? $params['images'] : null;

            \goodscommentModel::insert([
                'parent_id'     => $parent_id,
                'goods_id'      => $item->goods_id,
                'order_id'      => $order->id,
                'order_item_id' => $item->id,
                'member_id'     => $user->id,
                'level'         => $params['level'],
                'content'       => $params['content'],
                'images'        => $images ? implode(',', $images) : $images,
                'status'        => $comment_status,
                'created_at'    => timestamp(),
            ]);

            $item->comment_status = \orderitemModel::COMMENT_STATUS_OK;        // 评价成功
            $item->save();

            \orderactionModel::operAdd($order, $item, $user, 'user', '用户评价成功');

            // 订单评价后
            \Hook::emit('order_comment_after', ['order' => $order, 'item' => $item]);

            return $order;
        });

        return $order;
    }

    /************************************* 订单评价 stop *************************************/




    // 开始退款
    public static function startRefund($order, $item, $refund_money, $user = null, $remark = '')
    {
        $orderItem = \orderitemModel::find($item['id']);
        $orderItem->refund_status = \orderitemModel::REFUND_STATUS_OK;    // 同意退款
        $orderItem->refund_fee = $refund_money;
        $orderItem->save();

        \orderactionModel::operAdd($order, $item, $user, ($user ? 'admin' : 'system'), $remark . '，退款金额：' . $refund_money);
        \orderModel::refund($order, $orderItem, $refund_money, $remark, $user);

        if($order->type!='pickgoods') {
            // 订单退款后
            \Hook::emit('order_refund_after', ['order'=>$order, 'item'=>$orderItem]);
        }
    }


    // 退款
    public static function refund($order, $item, $refund_money, $remark = '', $user = [])
    {
        // 生成退款单
        $refundLog = new \refundlogModel();
        $refundLog->order_sn = $order->order_sn;
        $refundLog->refund_sn = \refundlogModel::getSn($order->member_id);
        $refundLog->order_item_id = $item->id;
        $refundLog->pay_fee = $order->pay_fee;
        $refundLog->refund_fee = $refund_money;
        $refundLog->pay_type = $order->pay_type;
        $refundLog->save();

        if($order->pay_type == 'pickgoods'){
            // 提货卡退款
            if ($refund_money > 0) {
                DB::transaction(function () use ($order, $refund_money, $user) {
                    // 提货卡余额退回
                    $delivery_card_id = $order->ext_arr['pickgoods_card_id'] ?? 0;
                    if ($delivery_card_id) {
                        $member_card = \app\modules\Admin\models\memberpickgoodscardModel::find($delivery_card_id);
                        if ($member_card) {
                            $before = $member_card->money;
                            $after = floatval(bcadd($before, $refund_money, 2));
                            DB::table('member_pickgoods_card')->where('id', $member_card->id)->update(['money' => $after]);
                            $member_pickgoods_log = [
                                'member_pickgoods_card_id' => $member_card->id,
                                'event' => 'refund',
                                'event_id' => $order->id,
                                'before' => $before,
                                'money' => $refund_money,
                                'after' => $after,
                                'oper_type' => 'admin',
                                'oper_id' => $user['id'],
                            ];
                            DB::table('member_pickgoods_log')->insert($member_pickgoods_log);
                        } else {
                            throw new \Exception('用户提货卡不存在');
                        }
                    }
                });
            }
        }else {
            if ($order->pay_type == 'wechat' || $order->pay_type == 'alipay') {
                $refundData = [
                    'refund_no'  => uniqid('ref'),
                    'trade_no'   => $order->transaction_id,
                    'total_fee'  => $order->pay_fee,
                    'refund_fee' => $refund_money,
                ];

                trace(json_encode($order));
                trace('refund refundData:' . json_encode($refundData), 'pay');

                $config     = \Yaf_Registry::get('config');
                $hostUrl    = $config->application->hostUrl;
                $notify_url = $hostUrl . "/pay/notifyr/payment/{$order->pay_type}/platform/{$order->platform}";
                $result     = (new \PayService($order->pay_type, $order->platform))->refund($refundData, $notify_url);

                trace('refund refundResult:' . json_encode($result), 'pay');

                if (!(isset($result['ret']) && $result['ret'] === 0)) {
                    throw new \Exception($result['msg']);
                }
                $refundLog->payment_json = json_encode($result['data']);
                $refundLog->save();
            } else if ($order->pay_type == 'wallet') {
                // 余额退款
                if ($refund_money > 0) {
                    \memberModel::money($refund_money, $order->member_id, 'refund', $order->id, $remark);
                }
            } else if ($order->pay_type == 'score') {
                // 积分退款，暂不支持积分退款
            } else if (strstr($order->pay_type, 'JoinPay')) {
                // TODO 汇聚支付退款
//            $result = (new JoinPay('refund'))->refund($order, $refundLog);
//            if ($result === true) {
//                //退款返还积分
//                if ($order->type == 'score') {
//                    if ($order->score_fee > 0) {
//                        // 退回积分
//                        User::score($order->score_fee, $order->user_id, 'refund', $order->id, '', [
//                            'order_id' => $order->id,
//                            'order_sn' => $order->order_sn,
//                        ]);
//                    }
//                }
//                return true;
//            } else {
//                throw new \Exception($result);
//            }
            }

            // 退款返还支付积分
            if ($order->score_fee > 0) {
                // 退回积分
                \memberModel::score($order->score_fee, $order->member_id, 'refund', $order->id, "退款返还积分 订单编号{$order->order_sn}");
            }

            // 兑换券支付订单返还
            if ($order->type != 'score' && bccomp($order->hongbao_fee, 0, 2) > 0) {
                \memberModel::hongbao($order->hongbao_fee, $order->member_id, 'refund', $order->id, "{$order->order_sn}订单退款，退还兑换券");
            }

            // 消费补贴支付订单返还
            if ($order->type != 'score' && bccomp($order->quanyi_fee, 0, 2) > 0) {
                \memberModel::quanyi($order->quanyi_fee, $order->member_id, 'tradeback', $order->id, "{$order->order_sn}订单退款，退还消费补贴");
            }
        }
        self::refundFinish($order, $item, $refundLog, $user);
        return true;
    }


    public static function refundFinish($order, $item, $refundLog, $user)
    {
        // 退款完成
        $refundLog->status = 1;
        $refundLog->save();

        // 退款完成
        $item->refund_status = \orderitemModel::REFUND_STATUS_FINISH;    // 退款完成
        $item->save();
        \orderactionModel::operAdd($order, $item, $user, 'admin', '退款成功');
    }


}